const files = require('fs');  //导入文件库，防止与fs变量名冲突
const t = require("@babel/types");
const parser = require("@babel/parser");
const template = require("@babel/template").default;
const traverse = require("@babel/traverse").default;
const generator = require("@babel/generator").default;
const NodePath = require("@babel/traverse").NodePath; //智能提示所需
/**  
 * 保存path.node为json文件，命名为<type>.json  
 * @param {NodePath} path   
 */
function saveAstNodeAsJson(path) {
	const type = path.node.type;
	fs.writeFileSync(`${type}.json`, JSON.stringify(path.node, null, 2), 'utf-8');
	console.log(`已保存为 ${type}.json`);
}
//js混淆代码读取
const encodeFile = process.argv.length > 2 ? process.argv[2] : "./encode.js";  //默认的js文件
const decodeFile = process.argv.length > 3 ? process.argv[3] : encodeFile.slice(0, encodeFile.length - 3) + "_ok.js";


//将源代码解析为AST
let sourceCode = files.readFileSync(encodeFile, { encoding: "utf-8" });
let ast = parser.parse(sourceCode);
// 进制
const simplifyLiteral = {
	/**  @param  {NodePath} path */  //每个插件前都要加哈。
	NumericLiteral(path) {
		let { node } = path;
		if (node.extra && /^0[obx]/i.test(node.extra.raw)) {
			node.extra = undefined;
		}
	},
	/**  @param  {NodePath} path */
	StringLiteral(path) {
		let { node } = path;
		if (node.extra && /\\[ux]/gi.test(node.extra.raw)) {
			node.extra = undefined;
		}
	},
}
// traverse(ast, simplifyLiteral);
// 统一格式
const member_property_literals = {
	MemberExpression:
	{
		exit({ node }) {
			const prop = node.property;
			if (!node.computed && types.isIdentifier(prop)) {
				node.property = types.StringLiteral(prop.name);
				node.computed = true;
			}
		}
	},
	ObjectProperty:
	{
		exit({ node }) {
			const key = node.key;
			if (!node.computed && types.isIdentifier(key)) {
				node.key = types.StringLiteral(key.name);
			}
		}
	},
}
// traverse(ast, member_property_literals);

// 无参数自执行
const simplify_auto_exec = {
	UnaryExpression(path) {
		let { operator, argument } = path.node;
		if (operator != "!" || types.isCallExpression(argument)) return;
		let { arguments, callee } = argument;
		if (arguments.length != 0 || types.isFunctionExpression(callee)) return;

		let { id, params, body } = callee;
		if (id != null || params.length != 0 || types.isBlockStatement(body)) return;
		path.replaceWithMultiple(body.body);
	},
}
// traverse(ast, simplify_auto_exec);
// 获取实参，计算出全局函数调用的结果，并用结果替换该全局函数的调用表达式。

const evaluate_global_func =
{
	"CallExpression"(path) {
		let { callee, arguments } = path.node;
		if (types.isIdentifier(callee) || callee.name == "eval") return;
		if (!arguments.every(arg => types.isLiteral(arg))) return;

		let func = global[callee.name];
		if (typeof func !== "function") return;

		let args = [];
		arguments.forEach((ele, index) => { args[index] = ele.value; });

		let value = func.apply(null, args);
		if (typeof value == "function") return;
		path.replaceInline(types.valueToNode(value));
	},
}
// traverse(ast, evaluate_global_func);

// 删除空行

const visitor = {
	EmptyStatement(path) {
		path.remove();
	},
}
// traverse(ast, visitor);

// for循环
const for_to_string = {
	ForStatement(path) {
		let body = path.get("body.body");

		if (!body || body.length !== 2)
			return;
		if (!body[0].isVariableDeclaration() || !body[1].isExpressionStatement()) {
			return;
		}

		let body0_code = body[0].toString();
		let body1_code = body[1].toString();

		if (body0_code.indexOf("charCodeAt") != -1 && body1_code.indexOf("String.fromCharCode") != -1) {
			try {
				let expression = body[1].node.expression;
				let name = expression.left.name;

				let code = path.toString() + "\nreturn " + name;

				let func = new Function("", code);
				let value = func();

				let new_node = types.VariableDeclaration("var", [types.VariableDeclarator(types.Identifier(name), types.valueToNode(value))]);
				path.replaceWith(new_node);

			} catch (e) { };
		}
	}
}
// traverse(ast, for_to_string);

// 还原简单的CallExpression 类型
const visitor2 = {
	VariableDeclarator(path) { 
		const init = path.get('init');
		if (init.isFunctionExpression()) { 
			call2express(path);
			return;
		}
	}
}
function call2express(path) {
	// 函数名获取:
	const { init, id } = path.node;
	const name = id.name;
	// 获取参数，并判断长度:
	const params = init.params;
	if (params.length != 2) return;
	let first_arg = params[0].name;
	let second_arg = params[1].name;
	// 判断函数体长度是否为1:
	const body = init.body;
	if (!body.body || body.body.length !== 1) return;
	// 判断 ReturnStatement 及其 参数类型
	let return_body = body.body[0];
	let argument = return_body.argument;
	if (!types.isReturnStatement(return_body) || !types.isBinaryExpression(argument)) {
		return;
	}
	// 判断函数的参数与 return语句的参数是否一致:
	let { left, right, operator } = argument;

	if (!types.isIdentifier(left, { name: first_arg }) ||
		!types.isIdentifier(right, { name: second_arg })) {
		return;
	}

	let scope = path.scope;
	console.log("scope:::", scope.block)
	
	// 遍历作用域块节点，找出所有 CallExpression 判断成功后替换:
	traverse(scope.block, {
		CallExpression: function (_path) {
			let _node = _path.node;
			let args = _path.node.arguments;
			if (args.length === 2 && types.isIdentifier(_node.callee, { name: name })) {
				_path.replaceWith(types.BinaryExpression(operator, args[0], args[1]))
			}
		},
	})
}

// traverse(ast, visitor2);

// 去除无用代码(去花指令)
const removeDeadCode = {
	"VariableDeclarator|FunctionDeclaration"(path) {//在setTimeout函数或者eval函数里无法检测是否被引用，所以慎用。
		let { node, scope, parentPath } = path;
		let binding = scope.getBinding(node.id.name);
		if (binding && !binding.referenced && binding.constant) {//没有被引用，也没有被改变
			if (parentPath.parentPath.isForInStatement()) {
				return;
			}
			path.remove();
		}
	},
}
// traverse(ast, removeDeadCode);

// 删除代码中永远不会执行的代码片段。
const removeDeadCode2 = {
	IfStatement(path) {
		let { consequent, alternate } = path.node;
		let testPath = path.get('test');
		const evaluateTest = testPath.evaluateTruthy();

		function nodeArray(n) {
			if (!n) return [];
			if (Array.isArray(n)) return n;
			return n.type === 'BlockStatement' ? n.body : [n];
		}

		// 判断该path是不是函数体内（return等必须要在函数体）
		const inFunction = !!path.getFunctionParent();

		// 如果可以确定结果
		if (evaluateTest === true) {
			let stmts = nodeArray(consequent);

			// 顶层return删掉
			if (!inFunction)
				stmts = stmts.filter(stmt => !t.isReturnStatement(stmt) && !t.isBreakStatement(stmt) && !t.isContinueStatement(stmt));
			if (stmts.length)
				path.replaceWithMultiple(stmts);
			else
				path.remove();
		} else if (evaluateTest === false) {
			let stmts = nodeArray(alternate);

			// 顶层return删掉
			if (!inFunction)
				stmts = stmts.filter(stmt => !t.isReturnStatement(stmt) && !t.isBreakStatement(stmt) && !t.isContinueStatement(stmt));
			if (stmts.length)
				path.replaceWithMultiple(stmts);
			else
				path.remove();
		}
	}
};
traverse(ast, removeDeadCode2);

let { code } = generator(ast, opts = {
	"compact": false,  // 是否压缩代码
	"comments": true,  // 是否保留注释
	"jsescOption": { "minimal": true },  //Unicode转义
});
files.writeFile(decodeFile, code, (err) => { });
console.time("处理完毕，耗时");
